﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BF_160
{
    public class MySerialPort
    { 
        private SerialPort cSerialPort;
        public string cPortName;
        public int cBand;
        private readonly object cMux;
        private int cSendCount = 0;
        private int cReceiveCount = 0;
        private readonly ConcurrentQueue<byte[]> cMessageQueue;
        private readonly EventWaitHandle cMessageWaitHandle;

        public MySerialPort(string portName)
        { 
            if(string.IsNullOrWhiteSpace(portName))
            {
                cPortName = "COM12";
            }
            cPortName = portName;

            if (cBand <= 0)
                cBand = 9600;

            cMux = new object();
            cReceiveCount = 0;
            cMessageQueue = new ConcurrentQueue<byte[]>();
            cMessageWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);

            OpenCom();

            ReceiveMsg();
        }

        private void OpenCom()
        {
            cSerialPort = new SerialPort(cPortName, cBand);
            if (cSerialPort.IsOpen)
                cSerialPort.Close();
            cSerialPort.ReadTimeout = 500;
            cSerialPort.WriteTimeout = 500;
            cSerialPort.StopBits =  StopBits.One;
            cSerialPort.Parity = Parity.None;


            cSerialPort.ReadBufferSize = 1024;
            cSerialPort.WriteBufferSize = 1024;

            cSerialPort.Open();
            Msg.WriteLine($"串口 [ {cPortName} ] 已打开!");

            cSerialPort.DiscardInBuffer();
            cSerialPort.DiscardOutBuffer();
        }

        #region 接受数据
        public void ReceiveMsg()
        {
            cSerialPort.DataReceived += (sender, e) =>
            {
                lock (cMux)
                {
                    if (cSerialPort.IsOpen == false)
                        return;
                    int len = cSerialPort.BytesToRead;
                    byte[] buff = new byte[len];
                    cSerialPort.Read(buff, 0, len);
                    cReceiveCount += len;
                    cMessageQueue.Enqueue(buff);
                    cMessageWaitHandle.Set();
                    Thread.Sleep(200);
                }
            };
        }
        #endregion

        #region 获取串口收到的数据
        public byte[] TryMessage(int msToTimeout = -1)
        {
            if (cMessageQueue.TryDequeue(out var message))
            {
                return message;
            }

            if (cMessageWaitHandle.WaitOne(msToTimeout))
            {
                if(cMessageQueue.TryDequeue(out message))
                {
                    return message;
                }
            }
            return default;
        }
        #endregion


        #region 发送消息
        public void Send(byte[] buff)
        {
            lock (cMux)
            {
                Thread.Sleep(10);
                Msg.WriteLine(" -> " + BitConverter.ToString(buff), 20);
                if (cSerialPort.IsOpen)
                    cSerialPort.Write(buff, 0, buff.Length);
                else
                {
                    throw new Exception("检测到端口被异常关闭,请关闭程序后重新启动!");
                }
                cSendCount += buff.Length;
            }

            #region 自测试 
            /*
            string fstr = string.Empty;
            byte[] fbuff;
            switch (buff[4])
            {
                case 0x00://焰条 
                    switch (buff[5])
                    {
                        case 0x41:
                            switch (buff[6])
                            {
                                case 0x01:
                                    fstr = "DB-CC-01-0B-00-A2-11-10-00-76-37";
                                    break;
                                case 0x02:
                                    fstr = "DB-CC-02-22-01-A1-00-00-00-00-00-00-00-00-00-00-04-20-00-00-00-00-00-00-00-00-00-00-00-00-00-00-91-61";
                                    break;
                            }
                            break;
                        case 0x47:
                            fstr = "DB-CC-01-08-00-A7-57-E1";
                            break;
                    }
                    break; 
                case 0x01: //左弹

                    switch (buff[5])
                    {
                        case 0x41:
                            switch (buff[6])
                            {
                                case 0x01:
                                    fstr = "DB-CC-02-0B-01-A2-11-10-01-79-44";
                                    break;
                                case 0x02:
                                    fstr = "DB-CC-02-22-01-A1-00-00-00-00-00-00-00-00-00-00-04-20-00-00-00-00-00-00-00-00-00-00-00-00-00-00-91-61";
                                    break;
                            }
                            break; 
                        case 0x47:
                            fstr = "DB-CC-02-08-01-A7-59-E7";
                            break; 
                    }
                    break;
                case 0x02: //右弹 
                    switch (buff[5])
                    {
                        case 0x41:

                            switch (buff[6])
                            {
                                case 0x01:
                                    fstr = "DB-CC-02-0B-02-A2-11-10-01-7A-49";
                                    break;
                                case 0x02:
                                    fstr = "DB-CC-02-22-02-A1-00-00-00-00-00-00-00-00-00-00-00-00-00-FF-FF-FF-C0-00-00-00-00-00-00-00-00-00-2B-B9";
                                    break;

                            }
                            break;
                        case 0x47:
                            fstr = "DB-CC-02-08-02-A7-5A-E9";
                            break;
                    }
                    break;
            }


            fstr = fstr.Replace("-", "");
            fbuff = GetByteArrayFromHexString(fstr);
            cMessageQueue.Enqueue(fbuff);
        }
        private byte[] GetByteArrayFromHexString(string hexString)
        {
            // 将16进制字符串两个字符一组转换成字节，忽略不足两个字符的组
            return Enumerable.Range(0, hexString.Length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(hexString.Substring(x, 2), 16))
                             .ToArray();
            */
            #endregion
        }
        #endregion

        #region 清空发送接受总数统计
        public void ClearCount()
        {
            lock (cMux)
            {
                cSendCount = 0;
                cReceiveCount = 0;
            }
        }
        #endregion

        #region 关闭串口
        public void Close()
        {
            cSerialPort.Close();
        }
        #endregion
    }
}
